///////////////////////////////////////////////////////////////////////////////
// DCTO8DVIDEO.C - Fonctions d'affichage pour dcto8d
// Author   : Daniel Coulom - danielcoulom@gmail.com
// Web site : http://dcto8.free.fr
// Pandora port : Sylvain FABRE
//
// This file is part of DCTO8D.
//
// DCTO8D is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// DCTO8D is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with DCTO8D. If not, see <http://www.gnu.org/licenses/>.
//
///////////////////////////////////////////////////////////////////////////////

#include <SDL.h>
#include <string.h>
#include <stdint.h>
#include "dcto8dglobal.h"

#ifdef NO_FULLSCREEN
#define VIDEO_MODE (SDL_SWSURFACE | SDL_DOUBLEBUF)
#else
#define VIDEO_MODE (SDL_FULLSCREEN | SDL_SWSURFACE | SDL_DOUBLEBUF)
#endif

// global variables //////////////////////////////////////////////////////////
SDL_Surface *screen = NULL;    //surface d'affichage de l'ecran
int xclient;                   //largeur fenetre utilisateur
int yclient;                   //hauteur ecran MO5 dans fenetre utilisateur
int xmouse;                    //abscisse souris dans fenetre utilisateur
int ymouse;                    //ordonnee souris dans fenetre utilisateur
int currentvideomemory;        //index octet courant en memoire video thomson
int currentlinesegment;        //numero de l'octet courant dans la ligne video
uint16_t *pcurrentpixel;            //pointeur ecran : pixel courant
uint16_t *pcurrentline;             //pointeur ecran : debut ligne courante
uint16_t *pmin;                     //pointeur ecran : premier pixel
uint16_t *pmax;                     //pointeur ecran : dernier pixel + 1
int screencount;               //nbre ecrans affiches entre 2 affichages status
int xpixel[XBITMAP + 1] __attribute__ ((aligned (32)));       //abscisse des pixels dans la ligne
void (*Displaysegment)();         //pointeur fonction decodage memoire video
static SDL_PixelFormat *globfmt;
extern char *pagevideo;     //pointeur page video affichee

//definition des intensites pour correction gamma anciennes valeurs dcmoto
//const int g[16]={0,60,90,110,130,148,165,180,193,205,215,225,230,235,240,255};
//definition des intensites pour correction gamma de la datasheet EF9369
int intens[16] __attribute__ ((aligned (32))) ={80,118,128,136,142,147,152,156,160,163,166,169,172,175,178,180};
int intensderiv[16] __attribute__ ((aligned (32))) ={ 255*(80-80)/100,  255*(118-80)/100, 255*(128-80)/100,
                                                       255*(136-80)/100,255*(142-80)/100, 255*(147-80)/100,
                                                       255*(152-80)/100,255*(156-80)/100, 255*(160-80)/100,
                                                       255*(163-80)/100,255*(166-80)/100, 255*(169-80)/100,
                                                       255*(172-80)/100,255*(175-80)/100, 255*(178-80)/100,
                                                       255*(180-80)/100};

extern void Drawstatusbar();
extern void SDL_error(int n);

// Modification de la palette ////////////////////////////////////////////////
void Palette(int n, int r, int v, int b)
{
  uint16_t curr = ((((uint8_t)(intensderiv[r])) >> globfmt->Rloss) << globfmt->Rshift) & globfmt->Rmask;
  curr |= ((((uint8_t)(intensderiv[v]))>> globfmt->Gloss) << globfmt->Gshift) & globfmt->Gmask;
  curr |= ((((uint8_t)(intensderiv[b]))>> globfmt->Bloss) << globfmt->Bshift) & globfmt->Bmask;
  psys->pcolor[n] = curr;
}

//Display screen /////////////////////////////////////////////////////////////
void Displayscreen(void)
{
  extern SDL_Surface *dialogbox;
  extern SDL_Surface *statusbar;
  extern SDL_Rect dialogrect;
  extern int dialog;
  if(dialog > 0)
    if (SDL_BlitSurface(dialogbox, NULL, screen, &dialogrect) < 0) SDL_error(31);
  if(--screencount < 0)  //1 fois sur 10 pour diminuer le temps d'affichage
  {
    if(statusbar != NULL)
    if(SDL_BlitSurface(statusbar, NULL, screen, NULL) < 0) SDL_error(32);
    screencount = 9;
  }
  if(SDL_Flip(screen) < 0) SDL_error(33);
}

// Decodage octet video mode 320x16 standard /////////////////////////////////
static inline void Decode320x16(void)
{
  int  i, k, c0, c1, color, shape;
  uint16_t c;
  shape = pagevideo[currentvideomemory | 0x2000];
  color = pagevideo[currentvideomemory++];
  c0 = (color & 0x07) | ((~color & 0x80) >> 4);        //background
  c1 = ((color >> 3) & 0x07) | ((~color & 0x40) >> 3); //foreground
  k = currentlinesegment << 4;
  for(i = 7; i >= 0; i--)
  {
    k += 2; c = psys->pcolor[(((shape >> i) & 1) ? c1 : c0)];
    while(pcurrentpixel < pcurrentline + xpixel[k])
      { *pcurrentpixel = c; pcurrentpixel++; }// memcpy(pcurrentpixel++, c, 4);
  }
}

// Decodage octet video mode bitmap4 320x200 4 couleurs //////////////////////
static inline void Decode320x4(void)
{
  int i, k, c0, c1;
  uint16_t c;
  c0 = pagevideo[currentvideomemory | 0x2000]; //color1
  c1 = pagevideo[currentvideomemory++];        //color2
  k = currentlinesegment << 4;
  for(i = 7; i >= 0; i--)
  {
    k += 2; c = psys->pcolor[(((c0 << 1) >> i & 2) | (c1 >> i & 1))];
    while(pcurrentpixel < pcurrentline + xpixel[k])
      { *pcurrentpixel = c; pcurrentpixel++; }// memcpy(pcurrentpixel++, c, 4);
  }
}

// Decodage octet video mode bitmap4 special 320x200 4 couleurs //////////////
static inline void Decode320x4special(void)
{
  int i, k, c0;
  uint16_t c;
  c0 = pagevideo[currentvideomemory | 0x2000] << 8;
  c0 |= pagevideo[currentvideomemory++] & 0xff;
  k = currentlinesegment << 4;
  for(i = 14; i >= 0; i -= 2)
  {
    k += 2; c = psys->pcolor[(c0 >> i & 3)];
    while(pcurrentpixel < pcurrentline + xpixel[k])
      { *pcurrentpixel = c; pcurrentpixel++; }// memcpy(pcurrentpixel++, c, 4);
  }
}

// Decodage octet video mode bitmap16 160x200 16 couleurs ////////////////////
static inline void Decode160x16(void)
{
  int i, k, c0;
  uint16_t c;
  c0 = pagevideo[currentvideomemory | 0x2000] << 8;
  c0 |= pagevideo[currentvideomemory++] & 0xff;
  k = currentlinesegment << 4;
  for(i = 12; i >= 0; i -= 4)
  {
    k += 4; c = psys->pcolor[(c0 >> i & 0x0f)];
    while(pcurrentpixel < pcurrentline + xpixel[k])
      { *pcurrentpixel = c; pcurrentpixel++; }// memcpy(pcurrentpixel++, c, 4);
  }
}

// Decodage octet video mode 640x200 2 couleurs //////////////////////////////
static inline void Decode640x2(void)
{
  int i, k, c0;
  uint16_t c;
  c0 = pagevideo[currentvideomemory | 0x2000] << 8;
  c0 |= pagevideo[currentvideomemory++] & 0xff;
  k = currentlinesegment << 4;
  for(i = 15; i >= 0; i--)
  {
    k += 1; c = psys->pcolor[(((c0 >> i) & 1) ? 1 : 0)];
    while(pcurrentpixel < pcurrentline + xpixel[k])
      { *pcurrentpixel = c; pcurrentpixel++; }// memcpy(pcurrentpixel++, c, 4);
  }
}

// Creation d'un segment de bordure ///////////////////////////////////////////
static inline void Displayborder(void)
{
  int k;
  uint16_t c;
  currentlinesegment++;
  c = psys->pcolor[psys->bordercolor];
  k = currentlinesegment << 4;
  while(pcurrentpixel < pcurrentline + xpixel[k])
    { *pcurrentpixel = c; pcurrentpixel++; }// memcpy(pcurrentpixel++, c, 4);
}

// Creation d'un segment de ligne d'ecran /////////////////////////////////////
#define DISPLAY_SEGMENT_COMMON_START() \
 int segmentmax; \
 segmentmax = psys->videolinecycle - 10; \
 if(segmentmax > 42) segmentmax = 42; \
 if(SDL_MUSTLOCK(screen)) \
   if(SDL_LockSurface(screen) < 0) {SDL_error(34); return;} \
 while(currentlinesegment < segmentmax) \
 { \
  if(psys->videolinenumber < 56) {Displayborder(); continue;}  \
  if(psys->videolinenumber > 255) {Displayborder(); continue;} \
  if(currentlinesegment == 0) {Displayborder(); continue;} \
  if(currentlinesegment == 41) {Displayborder(); continue;}

#define DISPLAY_SEGMENT_COMMON_END() \
    currentlinesegment++; \
 } \
 SDL_UnlockSurface(screen);

void Displaysegment320x16()
{
  DISPLAY_SEGMENT_COMMON_START();
  Decode320x16();
  DISPLAY_SEGMENT_COMMON_END();
}

void Displaysegment320x4()
{
  DISPLAY_SEGMENT_COMMON_START();
  Decode320x4();
  DISPLAY_SEGMENT_COMMON_END();
}

void Displaysegment320x4special()
{
  DISPLAY_SEGMENT_COMMON_START();
  Decode320x4special();
  DISPLAY_SEGMENT_COMMON_END();
}

void Displaysegment160x16()
{
  DISPLAY_SEGMENT_COMMON_START();
  Decode160x16();
  DISPLAY_SEGMENT_COMMON_END();
}

void Displaysegment640x2()
{
  DISPLAY_SEGMENT_COMMON_START();
  Decode640x2();
  DISPLAY_SEGMENT_COMMON_END();
}

// Changement de ligne ecran //////////////////////////////////////////////////
void Nextline(void)
{
  uint16_t *p0, *p1;
  if(SDL_MUSTLOCK(screen))
    if(SDL_LockSurface(screen) < 0) {SDL_error(35); return;}
  p1 = pmin + (psys->videolinenumber - 47) * yclient / YBITMAP * xclient;
  if(psys->videolinenumber == 263) p1 = pmax;
  p0 = pcurrentline;
  pcurrentline += xclient;
  while(pcurrentline < p1)
  {
    memcpy(pcurrentline, p0, 2 * xclient);
    pcurrentline += xclient;
  }
  if(p1 == pmax)
  {
    pcurrentline = pmin;    //initialisation pointeur ligne courante
    currentvideomemory = 0; //initialisation index en memoire video thomson
  }
  pcurrentpixel = pcurrentline;
  currentlinesegment = 0;
  SDL_UnlockSurface(screen);
}

// Resize screen //////////////////////////////////////////////////////////////
void Resizescreen(int x, int y)
{
  int i, savepause6809;
  extern int dialog;
  extern void Drawoptionbox();
  savepause6809 = psys->pause6809;
  psys->pause6809 = 1; SDL_Delay(200);
  //effacement surface de l'ecran
  if(screen != NULL)
  {
    pmin = (uint16_t*)(screen->pixels);
    pmax = pmin + screen->w * screen->h;
    for(pcurrentpixel = pmin; pcurrentpixel < pmax; pcurrentpixel++)
      memcpy(pcurrentpixel, psys->pcolor, 2);
    if(SDL_Flip(screen) < 0) SDL_error(36);
  }
  //creation nouvelle surface
  SDL_FreeSurface(screen);
  y -= YSTATUS;
  xclient = (x < 336) ? 336 : x;
  yclient = (y < 216) ? 216 : y;
  screen = SDL_SetVideoMode(xclient, yclient + YSTATUS, 16, VIDEO_MODE);
  if(screen == NULL)
  {
    screen = SDL_SetVideoMode(336, 216, 8, 0);
    SDL_WM_SetCaption(" Erreur fatale : Mode video non compatible", NULL);
    SDL_error(37);
  }
  globfmt = screen->format;

  pmin = (uint16_t*)(screen->pixels) + YSTATUS * xclient;
  pmax = pmin + (yclient * xclient);
  //rafraichissement de l'�cran
  pcurrentline = pmin;    //initialisation pointeur ligne courante
  pcurrentpixel = pmin;   //initialisation pointeur pixel courant
  currentlinesegment = 0; //initialisation numero d'octet dans la ligne
  currentvideomemory = 0; //initialisation index en memoire video thomson
  for(i = 0; i <= XBITMAP; i++) xpixel[i] = i * xclient / XBITMAP;
  psys->videolinecycle = 52;
  for(psys->videolinenumber = 48; psys->videolinenumber < 264; psys->videolinenumber++)
  { (*Displaysegment)(); Nextline();}
  psys->videolinecycle = 0; psys->videolinenumber = 0;
  Drawstatusbar();
  if(dialog == 2) Drawoptionbox();
  screencount = 0;
  Displayscreen();
  psys->pause6809 = savepause6809;
}
